// 封装哈希表类
function HashTable() {
    // 属性: 数组（链址法）、 记录当前数组元素个数、 数组长度
    this.storage = [];
    this.count = 0;
    this.limit = 7;

    // 方法
    // 1 哈希函数 (数据, 数组大小)
    HashTable.prototype.hashFunc = function (str, size) {
        // hashCode变量初始化
        let hashCode = 0;
    
        // 利用霍纳算法,计算hashCode,此处用UniCode编码
        for (let i = 0; i < str.length; i++) {
            hashCode = 37 * hashCode + str.charCodeAt(i);
            // 37为质数步长
        }
    
        // 压缩hashCode
        return hashCode % size
    }

    // 2 插入&修改操作
    HashTable.prototype.put = function (key, value) {
        // 1. 根据key获取索引值: 将数据插入到对应的位置
        let index = this.hashFunc (key, this.limit);

        // 2. 根据索引值取出bucket: a.如果bucket不存在,则创建
        let bucket = this.storage[index] ; 

        // 判断bucket 是否为空
        if (bucket == null) {
            this.storage[index] = [[key, value]];
            this.count ++;
            // this.storage[index].push([key, value])
            // bucket = [];
            // this.storage[index] = bucket;
            return
        }

        // 3. 判断是新增还是修改: 若已存在值,则为修改;若不存在,则添加
        for (let i = 0; i < bucket.length; i++) {
            if (bucket[i][0] === key) {
                bucket[i][1] = value;
                return
            }
        }

        // 添加操作
        bucket.push([key, value]);
        this.count ++;

        // 判断是否需要扩容
        if (this.count > this.limit * 0.75) {
            // 获取扩容质数
            let newPrime = this.getPrime(this.limit * 2)
            this.resize(newPrime)
        }
    }

    // 3 获取元素方法
    HashTable.prototype.get = function (key) {
        // 获取hashCode
        let index = this.hashFunc(key, this.limit);

        // 获取当前bucket
        let bucket = this.storage[index];

        // 判断是否存在
        if (bucket == null) {
            return null
        }

        for (let i = 0; i < bucket.length; i++) {
            if (bucket[i][0] === key) {
                return bucket[i][1]
            }
        }
        return null
    }

    // 4 删除操作
    HashTable.prototype.remove = function (key) {
        // 获取hashCode
        let index = this.hashFunc(key, this.limit);

        // 获取当前bucket
        let bucket = this.storage[index];

        // 判断是否存在
        if (bucket == null) {
            return null
        }

        for (let i = 0; i < bucket.length; i++) {
            if (bucket[i][0] === key) {
                let remValue = bucket[i][1];
                bucket.splice(i, 1);
                this.count --;

                // 判断是否需要缩容
                if (this.count >= 14 && this.count < this.limit * 0.25) {
                    // 获取质数
                    let newPrime = this.getPrime(Math.floor(this.limit / 2))
                    this.resize(newPrime)
                }
                return remValue
            }
        }
        return null
    }

    // 5 判断哈希表是否为空
    HashTable.prototype.isEmpty = function () {
        return this.count === 0
    }

    // 6 获取哈希表中元素的个数
    HashTable.prototype.size = function () {
        return this.count
    }

    // 7 哈希表容量更改(扩容/缩容)
    HashTable.prototype.resize = function (newLimit) {
        // 保存旧数据,更新哈希表
        let oldStorage = this.storage;
        let oldLimit = this.limit;
        this.storage = [];
        this.count = 0;
        this.limit = newLimit;

        // 遍历哈希表
        for (let i = 0; i < oldLimit; i++) {
            let bucket = oldStorage[i];

            // 判断是否存在数据
            if (bucket == null) {
                continue
            }

            for (let j = 0; j < bucket.length; j++) {
                // 调用put方法
                this.put(bucket[j][0], bucket[j][1]);
            }
        }
    }

    // 7.1 判断是否为质数
    HashTable.prototype.isPrime = function (num) {  
        // 开平方
        let temp = Math.floor(Math.sqrt(num)) 

        // 循环遍历
        for (let index = 2; index <= temp; index++) {
            if (num % index === 0 ){
                return false
            }
        }
        return true
    }

    // 7.2 获取质数
    HashTable.prototype.getPrime = function (num) {  
        while(!this.isPrime(num)){
            num++
        }
        return num
    }
}

// 测试哈希表

let ht = new HashTable();
ht.put('asd', '123');
ht.put('qwe', '123321');
ht.put('zccv', '978');
ht.put('dfh', '345');

console.log(ht)
// 获取大小
console.log(ht.isEmpty());
console.log(ht.size());

// 获取数据
console.log(ht.get('zccv'));

// 修改方法
ht.put('zccv', 999);
console.log(ht.get('zccv'));

// 删除方法
ht.remove('zccv');
console.log(ht.get('zccv'));

